package cz.possoft.explorer.resolver.method;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import cz.possoft.explorer.ServiceFactory;
import cz.possoft.explorer.meta.MethodMetadata;
import cz.possoft.explorer.meta.MethodMetadata.ParamMetadata;

/**
 * Concrete MethodResolver implementation, which uses reflection and it uses MethodKey type as the key type. 
 * 
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 * @version $Revision$
 */
public class MethodResolverImpl<V> implements MethodResolver<V,MethodKey>
{

   /**
    * @return MethodKey class, which is used as key class.
    */
   @Override
   public Class<MethodKey> getKeyClass()
   {      
      return MethodKey.class;
   }

   /**
    * Get names of all declared public methods for given class. V is actually unused for this type.
    * 
    * @param clazz parameter is used to obtain names of method attributes.
    * @param object This parameter is useless in MethodResolverImpl implementation 
    */   
   @Override
   public Map<MethodKey, MethodMetadata> getMethods(Class<? super V> clazz, V object)
   {
      Map<MethodKey, MethodMetadata> mapp = new HashMap<MethodKey, MethodMetadata>();
      Method[] methods = clazz.getDeclaredMethods();
      for (Method m : methods)
      {
         // don't count non-public methods and don't count getter methods without arguments (These are already used in fields part)
         if (!(Modifier.isPublic(m.getModifiers())))// || (m.getName().startsWith("get") && (m.getParameterTypes().length == 0)))
         {
            continue;
         }

         String name = m.getName();
         
         Class<?>[] paramClasses = m.getParameterTypes();
         Set<ParamMetadata> params = new LinkedHashSet<ParamMetadata>();
         
         for (Class<?> paramClass : paramClasses)
         {            
            boolean isConvertable = ServiceFactory.getInstance().getConverter().getStringConverterTypes().contains(paramClass);
            
            // name of parameters are null
            params.add(new ParamMetadata(null,paramClass,isConvertable));
         }
         MethodMetadata mm = new MethodMetadata(name, m, params);
         
         mapp.put(new MethodKey(name, paramClasses), mm);         
      }
      return mapp;           
   }

   /**
    * Invoking of method by reflection
    */
   @Override
   public Object invokeMethod(V object, MethodKey context, Object... params) throws Exception
   {      
      Method m = object.getClass().getMethod(context.getMethodName(), context.getClasses());
      return m.invoke(object, params);
   }   

}

